/**
 * Created by MLeo on 16/05/26.
 */
(function (w) {
    var _param = {
        "margin": {"top": 10, "bottom": 20, "left": 10, "right": 10},
    };

    w.initChart = function (param) {
        _param = extend(_param, param);
        getCanvasParam(_param);
        createCanvasDOM(_param);
        getAxisRegion(_param);
        initFrameParam(_param);
        drawCanvas(_param);
        touchEvent(_param);
    };


    function drawCanvas(param){
        drawFrame(param);
        drawPoint(param);
        drawXAxisText(param);
        drawYAxisText(param);
    }

    function touchEvent(param){
        touchStart(param)
        touchMove(param)
        touchEnd(param)
    }


    function touchStart(param) {
        param.obj.addEventListener("touchstart", function (e) {
            var event = e.touches[0];
            var crossPoint = getCrossPoint(param, event);
            if (crossPoint) {
                drawCross(param, crossPoint);
            }
        });
    }
    function touchEnd(param) {
        param.obj.addEventListener("touchend", function (e) {
            param.ctx.clearRect(0, 0, param.width, param.height);
            drawCanvas(param);
        });
    }

    function touchMove(param) {
        param.obj.addEventListener("touchmove", function (e) {
            param.ctx.clearRect(0, 0, param.width, param.height);
            drawCanvas(param);
            var event = e.touches[0];
            var crossPoint = getCrossPoint(param, event);
            if (crossPoint) {
                drawCross(param, crossPoint);
            }
        });
    }

    function getCrossPoint(param, event) {
        var x = event.pageX - param.obj.offsetLeft;
        var y = event.pageY - param.obj.offsetTop;
        var grid = param.grid[0];
        var startX = grid.startPoint.x;
        var startY = grid.startPoint.y;
        var unitX = grid.unitX;
        var unitY = grid.unitY;
        var index = Math.round((x - startX) / unitX);
        var series = param.series[0];
        if (index < 0 || index >= series.point.length) {
            return null;
        }
        var lineX = index * unitX + startX;
        var lineY = getCrossPointY(series, index);
        return {"x": lineX, "y": lineY, "i": index};
    }


    function getCrossPointY(series, index) {
        switch (series.type) {
            case "candlestick":
                return series.point[index].bar[1] + series.point[index].bar[3];
            case "line":
                return series.point[index].line[1];
            case "bar":
                return series.point[index].bar[1];
            default:
                break;
        }

    }


    function drawCross(param, point) {
        var ctx = param.ctx;
        ctx.save();
        ctx.beginPath();
        ctx.lineWidth = 0.2;
        ctx.moveTo(point.x, param.position.y);
        ctx.lineTo(point.x, param.position.y + param.canvasHeight);
        ctx.moveTo(param.position.x, point.y);
        ctx.lineTo(param.position.x + param.canvasWidth, point.y);
        ctx.stroke();
        ctx.restore();
    }

    function drawXAxisText(param) {
        var data = param.axisText.data;
        var filter = param.axisText.filter;
        var ctx = param.ctx;
        ctx.save();
        ctx.beginPath();
        ctx.strokeStyle = param.lineColor;
        ctx.lineWidth = param.lineWidth;
        ctx.font = param.font;
        ctx.fillStyle = param.fontColor;
        ctx.textAlign = "center";
        ctx.textBaseline = "top";
        var grid = param.grid[0];
        var x = grid.startPoint.x;
        var y = grid.startPoint.y;
        var unit = grid.unitX;
        for (var i = 0; i < data.length; i++) {
            var obj = data[i];
            var width = ctx.measureText(obj).width + 5;
            if (param.position.x < (x + unit * i - width / 2) && param.width > (x + unit * i + width / 2)) {
                if (filter) {
                    if (filter(i, obj)) {
                        ctx.moveTo(x + unit * i, y);
                        ctx.lineTo(x + unit * i, y + param.canvasHeight);
                        ctx.fillText(obj, x + unit * i, y + param.canvasHeight);
                    }
                } else {
                    ctx.moveTo(x + unit * i, y);
                    ctx.lineTo(x + unit * i, y + param.canvasHeight);
                    ctx.fillText(obj, x + unit * i, y + param.canvasHeight);
                    i += Math.round(width / unit);
                }
            }
        }
        ctx.stroke();
        ctx.restore();
    }

    function drawYAxisText(param) {
        var ctx = param.ctx;
        ctx.save();
        ctx.beginPath();
        ctx.strokeStyle = param.lineColor;
        ctx.lineWidth = param.lineWidth;
        ctx.font = param.font;
        ctx.fillStyle = param.fontColor;
        var grid = param.grid[0];
        var x = grid.position.x;
        var y = grid.position.y;
        if (grid.middle) {
            ctx.textAlign = "left";
            ctx.textBaseline = "middle";
            ctx.moveTo(x, y + grid.height / 2);
            ctx.lineTo(x + grid.width, y + grid.height / 2);
            ctx.fillText(grid.middle.toFixed(param.fractionalDigits), x + 5, y + grid.height / 2);
        }
        ctx.textAlign = "left";
        ctx.textBaseline = "top";
        ctx.fillText(grid.max.toFixed(param.fractionalDigits), x + 5, y);
        ctx.textAlign = "left";
        ctx.textBaseline = "bottom";
        ctx.fillText(grid.min.toFixed(param.fractionalDigits), x + 5, y + grid.height);
        ctx.stroke();
        ctx.restore();
    }


    function extend(a, b) {
        for (var key in b) {
            a[key] = b[key];
        }
        return a;
    }

    function drawPoint(param) {
        var series = param.series;
        for (var i = 0, len = series.length; i < len; i++) {
            var obj = series[i];
            switch (obj.type) {
                case "candlestick":
                    candlestickPoint(obj, param.grid[obj.gridIndex]);
                    drawCandlestick(param.ctx, obj.point, obj.pointColors);
                    break;
                case "line":
                    linePoint(obj, param.grid[obj.gridIndex]);
                    drawLine(param.ctx, obj.point, obj.pointColor);
                    break;
                case "bar":
                    barPoint(obj, param.grid[obj.gridIndex]);
                    drawBar(param.ctx, obj.point, obj.pointColors);
                    break;
                default:
                    break;
            }
        }
    }


    function drawCandlestick(ctx, candlesticks, colors) {
        ctx.save();
        for (var i = 0; i < candlesticks.length; i++) {
            var bar = candlesticks[i].bar;
            var line = candlesticks[i].line;
            ctx.beginPath();
            ctx.lineWidth = 1;
            ctx.fillStyle = colors[i];
            ctx.strokeStyle = colors[i];
            ctx.rect(bar[0], bar[1], bar[2], bar[3]);
            ctx.stroke();
            ctx.fill();

            ctx.beginPath();
            ctx.lineWidth = bar[2] / 5;
            ctx.strokeStyle = colors[i];
            ctx.moveTo(line[0], line[1]);
            ctx.lineTo(line[0], line[1] + line[2]);
            ctx.stroke();
        }
        ctx.restore();
    }

    function drawBar(ctx, bars, colors) {
        ctx.save();
        for (var i = 0; i < bars.length; i++) {
            var obj = bars[i];
            ctx.beginPath();
            ctx.lineWidth = 1;
            ctx.fillStyle = colors[i];
            ctx.strokeStyle = colors[i];
            ctx.rect(obj[0], obj[1], obj[2], obj[3]);
            ctx.stroke();
            ctx.fill();
        }
        ctx.restore();
    }

    function drawLine(ctx, lines, color) {
        ctx.save();
        ctx.beginPath();
        ctx.lineWidth = 1;
        ctx.strokeStyle = color;
        for (var i = 0; i < lines.length; i++) {
            var obj = lines[i];
            ctx.lineTo(obj[0], obj[1]);
        }
        ctx.stroke();
        ctx.restore();
    }

    function candlestickPoint(series, grid) {
        var candlesticks = [];
        var candlestickColors = [];
        for (var i = 0; i < series.data.length; i++) {
            var obj = series.data[i];
            var color = series.colors[0];
            if (obj[1] > obj[2]) {
                color = series.colors[1];
            }
            var bw = grid.unitX * 0.6;
            var bh = (obj[1] - obj[2]) * grid.unitY;
            var bx = grid.startPoint.x + grid.unitX * i - grid.unitX * 0.6 / 2;
            var by = grid.startPoint.y + grid.height - (obj[1] - grid.min) * grid.unitY;
            var bar = [bx, by, bw, bh];

            var lx = grid.startPoint.x + grid.unitX * i;
            var ly = grid.startPoint.y + grid.height - (obj[0] - grid.min) * grid.unitY;
            var lh = (obj[0] - obj[3]) * grid.unitY;
            var line = [lx, ly, lh];
            candlesticks.push({"bar": bar, "line": line});
            candlestickColors.push(color);
        }
        series.point = candlesticks;
        series.pointColors = candlestickColors;
    }

    function linePoint(series, grid) {
        var lines = [];
        for (var i = 0; i < series.data.length; i++) {
            var lx = grid.startPoint.x + grid.unitX * i
            var ly = grid.startPoint.y + grid.height - (series.data[i] - grid.min) * grid.unitY;
            var line = [lx, ly];
            lines.push(line);
        }
        series.point = lines;
        series.pointColor = series.color;
    }

    function barPoint(series, grid) {
        var bars = [];
        for (var i = 0; i < series.data.length; i++) {
            var bw = grid.unitX * 0.6;
            var bh = (series.data[i] - grid.min) * grid.unitY;
            var bx = grid.startPoint.x + grid.unitX * i - grid.unitX * 0.6 / 2
            var by = grid.startPoint.y + grid.height - (series.data[i] - grid.min) * grid.unitY;
            var bar = [bx, by, bw, bh];
            bars.push(bar);
        }
        series.point = bars;
        series.pointColors = series.colors;
    }

    function getAxisRegion(param) {
        var series = param.series;
        var datas = [];
        for (var i = 0, len = series.length; i < len; i++) {
            var obj = series[i];
            if (datas[obj.gridIndex]) {
                datas[obj.gridIndex] = datas[obj.gridIndex].concat(obj.data);
            } else {
                datas[obj.gridIndex] = obj.data;
            }
        }
        for (var i = 0, len = datas.length; i < len; i++) {
            var data = filterData(datas[i]);
            var middle = param.grid[i].middle;
            var scale = param.grid[i].scale;
            var max = Math.max.apply(null, data);
            var min = 0;
            if (scale) {
                min = Math.min.apply(null, data);
            }
            if (middle) {
                var interval = Math.max(Math.abs(middle - max), Math.abs(middle - min))
                interval = interval || middle * 0.1;
                max = middle + interval;
                min = middle - interval;
            }
            param.grid[i].max = max;
            param.grid[i].min = min;
        }
    }


    function filterData(data) {
        var ndata = [];
        var data = data.join(",").split(",");
        for (var i = 0, len = data.length; i < len; i++) {
            if (!isNaN(data[i])) {
                ndata.push(data[i]);
            }
        }
        return ndata;
    }

    function initFrameParam(param) {
        var pointNum = param.axisText.data.length;
        if (!param.boundary && pointNum > 1) {
            pointNum -= 1;
        }
        if (param.grid) {
            var height = 0;
            for (var i = 0, len = param.grid.length; i < len; i++) {
                var obj = param.grid[i];
                obj.width = param.canvasWidth;
                obj.height = param.canvasHeight * obj.ratio;
                obj.unitX = obj.width / pointNum;
                obj.unitY = obj.height / (obj.max - obj.min);
                obj.position = {"x": param.position.x, "y": param.position.y + height};
                obj.startPoint = {
                    "x": (param.position.x + (param.boundary ? obj.unitX / 2 : 0)),
                    "y": param.position.y + height
                };
                height += param.grid[i].height;
            }
        }
    }

    function drawFrame(param) {
        var ctx = param.ctx;
        ctx.save();
        for (var i = 0, len = param.grid.length; i < len; i++) {
            ctx.beginPath();
            ctx.lineWidth = param.lineWidth;
            ctx.strokeStyle = param.lineColor;
            ctx.rect(param.grid[i].position.x, param.grid[i].position.y, param.grid[i].width, param.grid[i].height);
            ctx.stroke();
        }
        ctx.restore();
    }


    function createCanvasDOM(param) {
        var canvas = document.createElement("canvas");
        canvas.width = param.width;
        canvas.height = param.height;
        param.obj.appendChild(canvas);
        param.canvas = canvas;
        param.ctx = canvas.getContext("2d");
    }

    function getCanvasParam(param) {
        var obj = document.getElementById(param.id);
        var width = obj.clientWidth;
        var height = obj.clientHeight;
        param.obj = obj;
        param.width = width;
        param.height = height;
        param.position = {"x": param.margin.left, "y": param.margin.top};
        param.canvasWidth = width - param.margin.left - param.margin.right;
        param.canvasHeight = height - param.margin.top - param.margin.bottom;
    }


})(window);
